using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Mytest
{
    /// <summary>
    /// 接口
    /// </summary>
    public interface ITest
    {
        string Add(string a, string b);
    }

    /// <summary>
    /// 实现
    /// </summary>
    public class Test : ITest
    {
        public string Add(string a, string b)
        {
            Console.WriteLine("Add方法被执行");
            return a + b;
        }
    }

    /// <summary>
    /// 需要自动实例化并且进行依赖注入的类
    /// </summary>
    public class MyClassController
    {
        private ITest _test;
        public MyClassController(ITest test)
        {
            _test = test;
        }
        /// <summary>
        /// 这是一个 Action
        /// </summary>
        /// <returns></returns>
        public string Action(string a, string b)
        {
            // 校验http请求的参数
            if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b))
                return "验证不通过";
            //开始运行
            var result = _test.Add(a, b);
            Console.WriteLine("NCC社区", "牛逼");
            // 响应结果
            return result;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {

            while (true)
            {
                string read = string.Empty;
                Console.WriteLine("用户你好，你要访问的控制器(不需要带Controller)");

                read = Console.ReadLine();

                // 检查是否具有此控制器并且获取 Type

                var hasController = IsHasController(read);

                // 找不到控制器，报 404 ，让用户重新请求
                if (!hasController.Item1)
                {
                    Console.WriteLine("404");
                    continue;
                }

                Console.WriteLine("控制器存在，请接着输入要访问的 Action");

                read = Console.ReadLine();

                // 检查是否具有此 Action 并且获取 Type
                bool hasAction = IsHasAction(hasController.Item2, read);

                // 找不到，继续报 404 
                if (hasAction == false)
                {
                    Console.WriteLine("404");
                    continue;
                }

                // 目前为止，URL存在，那么就是传递参数了

                Console.WriteLine("用户你好，URL 存在，请输入参数");
                Console.WriteLine("输入每个参数按一下回车键，结束输入请输入0再按下回车键");

                // 开始接收用户输入的参数
                List<object> paramList = new List<object>();
                while (true)
                {
                    string param = Console.ReadLine();
                    if (param == "0")
                        break;
                    paramList.Add(param);
                }

                Console.WriteLine("输入结束，正在发送 http 请求 \n");

                // 用户的请求已经校验通过并且开始，现在来继续仿 ASP.NET Core 执行

                object response = StartASPNETCORE(hasController.Item2, read, paramList.ToArray());

                Console.WriteLine("执行结果是：");
                Console.WriteLine(response);


                Console.ReadKey();
            }
        }

        private static Assembly assembly = Assembly.GetCallingAssembly();
        private static Type[] types;
        static Program()
        {
            types = assembly.GetTypes();
        }

        /// <summary>
        /// 判断是否有此控制器，并且返回 Type
        /// </summary>
        /// <param name="controllerName">控制器名称(不带Controller)</param>
        /// <returns></returns>
        private static (bool, Type) IsHasController(string controllerName)
        {
            // 不分大小写

            string name = controllerName + "Controller";
            if (!types.Any(x => x.Name.ToLower() == name.ToLower()))
                return (false, null);

            return (true, types.FirstOrDefault(x => x.Name.ToLower() == name.ToLower()));
        }

        /// <summary>
        /// 判断一个控制器中是否具有此方法
        /// </summary>
        /// <param name="type">控制器类型</param>
        /// <param name="actionName">Action名称</param>
        /// <returns></returns>
        private static bool IsHasAction(Type type, string actionName)
        {
            // 不分大小写

            return type.GetMethods().Any(x => x.Name.ToLower() == actionName.ToLower());
        }

        /// <summary>
        /// 实例化依赖
        /// </summary>
        /// <param name="type">要被实例化依赖注入的类型</param>
        public static object[] CreateType(Type type)
        {
            // 这里只使用一个构造函数
            ConstructorInfo construct = type.GetConstructors().FirstOrDefault();

            // 获取类型的构造函数参数
            ParameterInfo[] paramList = construct.GetParameters();

            // 依赖注入的对象列表
            List<object> objectList = new List<object>();

            // 为构造函数的每个参数类型，实例化一个类型
            foreach (ParameterInfo item in paramList)
            {
                //获取参数类型：item.ParameterType.Name

                // 获取程序中，哪个类型实现了 item 的接口

                Type who = types.FirstOrDefault(x => x.GetInterfaces().Any(z => z.Name == item.ParameterType.Name));

                // 实例化
                object create = Activator.CreateInstance(who, new object[] { });
                objectList.Add(create);
            }
            return objectList.ToArray();
        }
        /// <summary>
        /// 实现依赖注入、调用方法
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="actionName">方法名称</param>
        /// <param name="paramList">调用方法的参数列表</param>
        /// <returns></returns>
        private static object StartASPNETCORE(Type type, string actionName, params object[] paramList)
        {
            // 获取 Action 重载方法 
            // 名字一样，参数个数一致
            MethodInfo method = type.GetMethods()
                .FirstOrDefault(x => x.Name.ToLower() == actionName.ToLower()
                && x.GetParameters().Length == paramList.Length);

            // 参数有问题，找不到合适的 Action 重载进行调用
            // 报 405 
            if (method == null)
                return "405";

            // 实例化控制器

            // 获取依赖对象
            object[] inject = CreateType(type);
            // 注入依赖，实例化对象
            object example = Activator.CreateInstance(type, inject);

            // 执行方法并且返回执行结果
            object result;
            try
            {
                result = method.Invoke(example, paramList);
                return result;
            }
            catch
            {
                // 报 500
                result = "500";
                return result;
            }
        }
    }
}